package com.emeraldparser.symbol;

import com.emeraldparser.util.Automaton;
import com.emeraldparser.util.Automatons;
import com.emeraldparser.util.HashCodes;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

/**
 * 
 * @author Adam Paynter
 *
 */
public final class OptionalSymbolClass extends TwoStateSymbolClass {

	private final SymbolClass optionalSymbolClass;
	private final transient int hashCode;

	public OptionalSymbolClass(SymbolClass optionalSymbolClass) {
		this.optionalSymbolClass = Preconditions.checkNotNull(optionalSymbolClass, "Optional symbol class must not be null");
		this.hashCode = HashCodes.classSpecificHashCode(OptionalSymbolClass.class, optionalSymbolClass);
	}

	public SymbolClass getOptionalSymbolClass() {
		return optionalSymbolClass;
	}

	@Override
	public Automaton<? extends State, ? extends SymbolClass> toAutomaton() {
		Automaton<State, SymbolClass> automaton = Automatons.create(super.toAutomaton());
		
		automaton.addDirectedEdge(getBeforeState(), getAfterState(), optionalSymbolClass);
		automaton.addDirectedEdge(getBeforeState(), getAfterState(), null);
		
		return automaton;
	}

	@Override
	public String toString() {
		return optionalSymbolClass + "?";
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (getClass() != obj.getClass()) {
			return false;
		}
		
		OptionalSymbolClass other = (OptionalSymbolClass) obj;
		
		return Objects.equal(optionalSymbolClass, other.optionalSymbolClass);
	}

	@Override
	public <I, O> O accept(SymbolClassVisitor<I, O> visitor, I input) {
		return visitor.visitOptionalSymbolClass(this, input);
	}

}
